<?php

abstract class Common_Chart_Strategy_Google_AxisChart extends Common_Chart_Strategy_Google {

    public function __construct() {
        parent::__construct();
    }
    
    protected function apply_render_options($arr_chart_parameters, $arr_data, $arr_headers, $arr_options = array()) {

        $arr_chart_parameters = parent::apply_render_options($arr_chart_parameters, $arr_data, $arr_headers, $arr_options);
        $arr_chart_parameters = $this->apply_axis_options($arr_chart_parameters, $arr_headers, $arr_options);
        $arr_chart_parameters = $this->apply_grid_options($arr_chart_parameters, $arr_data, $arr_options);
        $arr_chart_parameters = $this->apply_legend_options($arr_chart_parameters, $arr_data, $arr_options);

        return $arr_chart_parameters;
    }

    protected function apply_legend_options($arr_chart_parameters, $arr_data, $arr_options = array()) {

        if(!isset($arr_options['show_legend']) || (isset($arr_options['show_legend']) && ($arr_options['show_legend'] == true))) {

            $arr_series_to_use = $this->get_series_definitions($arr_options);

            // is there at least one data series, or forced to show legend?
            if(
                (count($arr_series_to_use) > 1) ||
                (isset($arr_options['show_legend']) && ($arr_options['show_legend'] == true))
            ) {
                
                $arr_series_names = array();
                foreach($arr_data as $str_key => $arr) {
                    if(isset($arr_series_to_use[$str_key])) {
                        $arr_series_names[$str_key] = $arr_series_to_use[$str_key]['label'];
                    }
                }

                $arr_chart_parameters[self::PARAMETER_LEGEND_KEY] = implode('|', $arr_series_names);
            }

            if(isset($arr_options['legend_position'])) {
                $enm_legend_position = $this->convert_legend_position($arr_options['legend_position']);
                if(!empty($enm_legend_position)) {
                    $arr_chart_parameters[self::PARAMETER_LEGEND_POSITION] = $enm_legend_position;
                }
            }
            
        }

        return $arr_chart_parameters;
    }

    protected function get_chart_ymin($arr_options = array()) {

        $int_data_min = $this->get_data_min();
        $int_chart_min = $int_data_min;

        // case: specific number
        if(isset($arr_options['yaxis_min']) && is_numeric($arr_options['yaxis_min'])) {
            $int_chart_min = (int) $arr_options['yaxis_min'];
        }

        // case: padding from data
        else if(
            (isset($arr_options['yaxis_padding']) && is_numeric($arr_options['yaxis_padding'])) ||
            (isset($arr_options['yaxis_min_padding']) && is_numeric($arr_options['yaxis_min_padding']))
        ) {

            $flt_percentage = 0;
            if(isset($arr_options['yaxis_min_padding'])) {
                $flt_percentage = (float) $arr_options['yaxis_min_padding'];
            } else if(isset($arr_options['yaxis_padding'])) {
                $flt_percentage = (float) $arr_options['yaxis_padding'];
            }

            if(($flt_percentage >= 0) && ($flt_percentage <= 100)) {
                $int_chart_min = $int_chart_min - ($int_chart_min * ($flt_percentage / 100));
            }
        }

        // case: automatic
        else {

            // determine the chart min (slightly below the data min)
            $int_pow = $this->get_pow((string) $int_data_min);
            $int_temp = (int) ($int_data_min / pow(10, $int_pow));
            $int_temp--;
            $int_temp = $int_temp * pow(10, $int_pow);
            $int_chart_min = $int_temp;

            // if the data min is positive, then ensure the chart min is >= 0
            if(($int_data_min >= 0) && ($int_chart_min < 0)) {
                $int_chart_min = 0;
            }
        }

        return $int_chart_min;
    }
    protected function get_chart_ymax($arr_options = array()) {

        $int_data_max = $this->get_data_max();
        $int_chart_max = $int_data_max;

        // case: specific number
        if(isset($arr_options['yaxis_max']) && is_numeric($arr_options['yaxis_max'])) {
            $int_chart_max = (int) $arr_options['yaxis_max'];
        }

        // case: padding from data
        else if(
            (isset($arr_options['yaxis_padding']) && is_numeric($arr_options['yaxis_padding'])) ||
            (isset($arr_options['yaxis_max_padding']) && is_numeric($arr_options['yaxis_max_padding']))
        ) {

            $flt_percentage = 0;
            if(isset($arr_options['yaxis_max_padding'])) {
                $flt_percentage = (float) $arr_options['yaxis_max_padding'];
            } else if(isset($arr_options['yaxis_padding'])) {
                $flt_percentage = (float) $arr_options['yaxis_padding'];
            }

            if(($flt_percentage >= 0) && ($flt_percentage <= 100)) {
                $int_chart_max = $int_chart_max + ($int_chart_max * ($flt_percentage / 100));
            }
        }

        // case: automatic
        else {

            // determine the chart max (slightly above the data max)
            $int_pow = $this->get_pow((string) $int_data_max);
            $int_temp = (int) ($int_data_max / pow(10, $int_pow));
            $int_temp++;
            $int_temp = $int_temp * pow(10, $int_pow);
            $int_chart_max = $int_temp;

        }
        
        return $int_chart_max;
    }

    protected function apply_axis_options($arr_chart_parameters, $arr_headers, $arr_options = array()) {
        
        $int_yaxis_min = $this->get_chart_ymin($arr_options);
        $int_yaxis_max = $this->get_chart_ymax($arr_options);

        $str_chart_scale = $int_yaxis_min . ',' . $int_yaxis_max;

        $arr_chart_parameters[self::PARAMETER_CHART_DATA_SCALE] = $str_chart_scale;

        $bln_include_labels = isset($arr_options['include_labels']) ? $arr_options['include_labels'] : true;
        
        //----------------------------------------------------------------------
        // y-axis (numbers)
        //----------------------------------------------------------------------

        if($bln_include_labels) {
        
            $arr_chart_parameters[self::PARAMETER_CHART_AXIS_VISIBLE] = 'y';
    
            $str_label_suffix = isset($arr_options['yaxis_label_suffix'])
                ? $arr_options['yaxis_label_suffix']
                : null;
    
            // case: y-axis values are > 1 million
            if(($int_yaxis_max/1000) > 1) {
    
                // shorten the length of the numbers by converting into thousands
                $int_yaxis_min = (int) ($int_yaxis_min/1000);
                $int_yaxis_max = (int) ($int_yaxis_max/1000);
    
                // format the axis values with a thousands separator and a 'K' suffix
                // because we have divided by 1000 in the axis scale
                //
                // 0 - first visible axis
                // N*...* - required by google
                // s - display group separators
                // K - literal suffix text
    
                $str_axis_style = '0N*s*K' . $str_label_suffix;
    
                $arr_chart_parameters[self::PARAMETER_AXIS_STYLE] = $str_axis_style;
    
            }
    
            // case: y-axis values are < 1 million
            else {
    
                $str_axis_style = '0N*s*' . $str_label_suffix;
    
                // format the axis values with a thousands separator
                $arr_chart_parameters[self::PARAMETER_AXIS_STYLE] = $str_axis_style;
    
            }
    
            // 0 (zero) denotes the first visible axis of [chxt]
            $str_chart_axis_range = '0,' . $int_yaxis_min . ',' . $int_yaxis_max;
    
            if(isset($arr_options['yaxis_step']) && is_numeric($arr_options['yaxis_step'])) {
                $int_step = (int) $arr_options['yaxis_step'];
                $str_chart_axis_range .= ',' . $int_step;
            }
    
            $arr_chart_parameters[self::PARAMETER_CHART_AXIS_RANGE] = $str_chart_axis_range;

        }
        
        //----------------------------------------------------------------------
        // x-axis groups
        //----------------------------------------------------------------------

        if($bln_include_labels) {
        
            // modify the x-axis label in order to fit as many labels as possible
            if(!isset($arr_options['xaxis_autofit_labels'])) {
                $arr_options['xaxis_autofit_labels'] = true;
            }

            if($arr_options['xaxis_autofit_labels'] == true) {
    
                $int_chart_width = $arr_options['width'];
                $int_label_width = $this->calculate_label_width($arr_headers);
    
                $int_frequency = 1;
                $arr_headers_modified = $arr_headers;
    
                $bln_try_alternating = !isset($arr_options['xaxis_alternate_labels']) || ($arr_options['xaxis_alternate_labels'] == true);
    
                // continue to simplify headers (by removing every X label) until its
                // calculated width is smaller than the chart width
                $i = 0;
                while(($int_label_width > $int_chart_width) && ($i < 15)) {

                    $int_frequency++;
                    $arr_headers_modified = $this->filter_headers($arr_headers, $int_frequency);
                    $int_label_width = $this->calculate_label_width($arr_headers_modified);
    
                    // if the width is still too large, try alternating the headers
                    if($bln_try_alternating && ($int_label_width > $int_chart_width)) {
    
                        // this will alternate the headers into two rows
                        $arr_headers_alternate = $this->alternate_headers($arr_headers_modified);
    
                        // find the largest width of the header rows
                        $int_alternate_width = 0;
                        foreach($arr_headers_alternate as $arr) {
                            $int_width = $this->calculate_label_width($arr);
                            if($int_width > $int_alternate_width) {
                                $int_alternate_width = $int_width;
                            }
                        }
    
                        // if this is smaller than the chart width, then we can stop, and
                        // know to alternate the labels
                        if($int_alternate_width < $int_chart_width) {
                            $arr_options['xaxis_alternate_labels'] = true;
                            $arr_headers = $arr_headers_modified;
                            break;
                        }
    
                    }
                    $i++;
                }
    
                if($int_frequency > 1) {
                    $arr_options['xaxis_label_frequency'] = $int_frequency;
                }
    
            }
            

            //----------------------------------------------------------------------
    
            if(isset($arr_options['xaxis_label_frequency']) && is_numeric($arr_options['xaxis_label_frequency'])) {
    
                $int_frequency = $arr_options['xaxis_label_frequency'];
                $arr_headers = $this->filter_headers($arr_headers, $int_frequency);
                $arr_chart_parameters[self::PARAMETER_CHART_LABELS] = implode('|', $arr_headers);
    
            }
    
            //----------------------------------------------------------------------
    
            if(isset($arr_options['xaxis_alternate_labels']) && ($arr_options['xaxis_alternate_labels'] == true)) {
    
                $i = 0;
                $int_alternate_max = 1;
    
                $arr_headers_modified = array();
                foreach($arr_headers as $str_label) {
    
                    if(empty($str_label)) {
                        for($j = 0; $j <= $int_alternate_max; $j++) {
                            $arr_headers_modified[$j][] = '';
                        }
                    } else {
                        for($j = 0; $j <= $int_alternate_max; $j++) {
                            $arr_headers_modified[$j][] = ($i == $j) ? $str_label : '';
                        }
                        $i++;
                    }
    
                    if($i > $int_alternate_max) {
                        $i = 0;
                    }
    
                }
    
                unset($arr_chart_parameters[self::PARAMETER_CHART_LABELS]);
    
                // overwrite this parameter to include both axes, ensure the order merely
                // appends the x-axis to what was previously there
                $arr_chart_parameters[self::PARAMETER_CHART_AXIS_VISIBLE] = 'y,x,x';
    
                // use custom axis labels for the periods
                // 1 (one) denotes the second visible axis of [chxt]
    
                $str_value = '1:|' . implode('|', $arr_headers_modified[0]) . '|2:|' . implode('|', $arr_headers_modified[1]);
                $arr_chart_parameters[self::PARAMETER_CHART_LABELS_CUSTOM] = $str_value;
    
                $str_axis_styles = isset($arr_chart_parameters[self::PARAMETER_AXIS_STYLE])
                    ? $arr_chart_parameters[self::PARAMETER_AXIS_STYLE]
                    : '';
    
                if(!empty($str_axis_styles)) {
                    $str_axis_styles .= '|';
                }
    
                $arr_chart_parameters[self::PARAMETER_AXIS_STYLE] = $str_axis_styles . '1,777777,11.5,0,tl|2,777777,11.5,0,tl';
    
            }
            
        }
        
        return $arr_chart_parameters;
    }

    public function apply_grid_options($arr_chart_parameters, $arr_data, $arr_options = array()) {
        
        //----------------------------------------------------------------------
        // grid-lines (horizontal)
        //----------------------------------------------------------------------

        $int_chart_grid_vertical_step = 0;
        if(isset($arr_options['chart_grid_vertical_step']) && is_numeric($arr_options['chart_grid_vertical_step'])) {
            $int_chart_grid_vertical_step = (int) $arr_options['chart_grid_vertical_step'];
            $int_chart_grid_vertical_step = round((100 / $int_chart_grid_vertical_step), 5);
        }

        $int_chart_grid_horizontal_step = 20;
        if(isset($arr_options['chart_grid_horizontal_step']) && is_numeric($arr_options['chart_grid_horizontal_step'])) {
            $int_chart_grid_horizontal_step = (int) $arr_options['chart_grid_horizontal_step'];
            $int_chart_grid_horizontal_step = round((100 / $int_chart_grid_horizontal_step), 5);
        }

        $arr_chart_parameters[self::PARAMETER_CHART_GRID_LINES] = $int_chart_grid_vertical_step . ',' . $int_chart_grid_horizontal_step . ',1,5';

        return $arr_chart_parameters;
    }

    protected function filter_headers($arr_headers, $int_frequency = 1) {

        if($int_frequency > 1) {

            $i = 0;
            $arr_headers_temp = array();

            // start from the end (closest to the right-side of the x-axis)
            $arr_headers = array_reverse($arr_headers);

            foreach($arr_headers as $str_label) {
                if(($i%$int_frequency) != 0) {
                    $str_label = '';
                }
                $arr_headers_temp[] = $str_label;
                $i++;
            }

            $arr_headers = array_reverse($arr_headers_temp);

        }

        return $arr_headers;
    }

    protected function calculate_label_width($arr_headers) {
        $int_label_width = 0;
        foreach($arr_headers as $str_label) {
            $int_label_width += strlen($str_label) * 7;
        }
        
        return $int_label_width;
    }

    protected function alternate_headers($arr_headers, $int_alternate_max = 2) {

        $i = 0;
        $int_alternate_max = (int) $int_alternate_max;

        $arr_headers_modified = array();
        foreach($arr_headers as $str_label) {

            if(empty($str_label)) {
                for($j = 0; $j < $int_alternate_max; $j++) {
                    $arr_headers_modified[$j][] = '';
                }
            } else {
                for($j = 0; $j < $int_alternate_max; $j++) {
                    $arr_headers_modified[$j][] = ($i == $j) ? $str_label : '';
                }
                $i++;
            }

            if($i >= $int_alternate_max) {
                $i = 0;
            }

        }

        return $arr_headers_modified;

    }

    public function has_feature($str_feature_key) {

        $bln = false;

        switch($str_feature_key) {
        case Common_Chart::FEATURE_LEGEND_POSITION;
            if($this->count_data_series() > 1) {
                $bln = true;
            }
            break;
        case Common_Chart::FEATURE_ZERO_YAXIS;
            $bln = true;
            break;
        }

        return $bln;
    }

}
